package store

import (
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"path/filepath"
	"time"

	"github.com/astaxie/beego"
)

type Store123 struct {
	Token     string // API Token
	Host      string // API域名
	UploadDir string // 上传目录
}

var ModelStore123 = &Store123{}

func init() {
	ModelStore123.Token = beego.AppConfig.String("123::token")
	ModelStore123.Host = beego.AppConfig.String("123::host")
	ModelStore123.UploadDir = beego.AppConfig.String("123::upload_dir")

	if ModelStore123.Host == "" {
		ModelStore123.Host = "https://open-api.123pan.com"
	}
}

// 判断是否启用123云盘存储
func (this *Store123) IsEnable() bool {
	return beego.AppConfig.DefaultBool("123::enable", false)
}

// 上传文件到123云盘
func (this *Store123) Upload(fileName string, fileData []byte) (string, error) {
	beego.Info("[123云盘] 开始上传文件:", fileName)

	if !this.IsEnable() {
		beego.Error("[123云盘] 存储未启用")
		return "", errors.New("123云盘存储未启用")
	}

	if this.Token == "" {
		beego.Error("[123云盘] Token未配置")
		return "", errors.New("123云盘Token未配置")
	}

	// 1. 计算文件MD5
	beego.Info("[123云盘] 计算文件MD5...")
	hash := md5.New()
	hash.Write(fileData)
	fileMd5 := hex.EncodeToString(hash.Sum(nil))
	beego.Info("[123云盘] 文件MD5:", fileMd5)

	// 2. 创建文件
	beego.Info("[123云盘] 创建文件...")
	createResp, err := this.createFile(fileName, int64(len(fileData)), fileMd5)
	if err != nil {
		beego.Error("[123云盘] 创建文件失败:", err)
		return "", err
	}
	beego.Debug("[123云盘] 创建文件响应:", createResp)

	// 如果是秒传，直接返回直链
	if createResp.Data.Reuse && createResp.Data.FileId > 0 {
		beego.Info("[123云盘] 文件秒传成功")
		return this.getDirectLink(fmt.Sprintf("%d", createResp.Data.FileId))
	}

	// 3. 获取上传地址
	beego.Info("[123云盘] 获取上传地址...")
	uploadUrl, err := this.getUploadUrl(createResp.Data.PreUploadId, 0)
	if err != nil {
		beego.Error("[123云盘] 获取上传地址失败:", err)
		return "", err
	}
	beego.Debug("[123云盘] 上传地址:", uploadUrl)

	// 4. 上传文件
	beego.Info("[123云盘] 开始上传文件内容...")
	err = this.uploadFile(uploadUrl, fileData)
	if err != nil {
		beego.Error("[123云盘] 上传文件内容失败:", err)
		return "", err
	}
	beego.Info("[123云盘] 文件内容上传完成")

	// 5. 完成上传
	beego.Info("[123云盘] 完成上传...")
	completeResp, err := this.completeUpload(createResp.Data.PreUploadId)
	if err != nil {
		beego.Error("[123云盘] 完成上传失败:", err)
		return "", err
	}

	// 完成上传后获取直链
	if completeResp.Data.Complete && completeResp.Data.FileId > 0 {
		beego.Info("[123云盘] 获取文件直链...")
		directUrl, err := this.getDirectLink(fmt.Sprintf("%d", completeResp.Data.FileId))
		if err != nil {
			beego.Error("[123云盘] 获取直链失败:", err)
			return "", err
		}
		beego.Info("[123云盘] 获取直链成功:", directUrl)
		return directUrl, nil
	}

	return "", errors.New("文件上传未完成")
}

// 创建文件
func (this *Store123) createFile(fileName string, fileSize int64, fileMd5 string) (*CreateFileResponse, error) {
	url := fmt.Sprintf("%s/upload/v1/file/create", this.Host)
	beego.Info("[123云盘] 创建文件请求:", url)
	data := map[string]interface{}{
		"filename":     filepath.Base(fileName),
		"parentFileID": 10509224, // 默认父目录ID
		"size":         fileSize,
		"etag":         fileMd5,
	}
	beego.Debug("[123云盘] 创建文件请求数据:", data)
	resp, err := this.doRequest("POST", url, data)
	if err != nil {
		return nil, err
	}
	beego.Debug("[123云盘] 创建文件响应:", string(resp))
	var result CreateFileResponse
	if err := json.Unmarshal(resp, &result); err != nil {
		beego.Error("[123云盘] 创建文件失败:", err)
		return nil, err
	}

	beego.Debug("[123云盘] 创建文件响应:", &result)
	return &result, nil
}

// 获取上传地址
func (this *Store123) getUploadUrl(preUploadId string, sliceNo int) (string, error) {
	url := fmt.Sprintf("%s/upload/v1/file/get_upload_url", this.Host)
	beego.Info("[123云盘] 获取上传地址请求:", url)

	data := map[string]interface{}{
		"preuploadID": preUploadId,
		"sliceNo":     sliceNo + 1, // 分片序号从1开始
	}

	beego.Debug("[123云盘] 获取上传地址请求数据:", data)

	resp, err := this.doRequest("POST", url, data)
	if err != nil {
		return "", err
	}

	beego.Debug("[123云盘] 获取上传地址响应:", string(resp))

	var result struct {
		Code    int    `json:"code"`
		Message string `json:"message"`
		Data    struct {
			UploadUrl string `json:"presignedURL"` // 修改字段名为presignedURL
		} `json:"data"`
	}

	if err := json.Unmarshal(resp, &result); err != nil {
		return "", err
	}

	if result.Code != 0 {
		return "", fmt.Errorf("获取上传地址失败: %s", result.Message)
	}

	beego.Debug("[123云盘] 获取上传地址响应:", result)
	return result.Data.UploadUrl, nil
}

// 上传文件
func (this *Store123) uploadFile(uploadUrl string, fileData []byte) error {
	beego.Info("[123云盘] 上传文件请求:", uploadUrl)

	req, err := http.NewRequest("PUT", uploadUrl, bytes.NewReader(fileData))
	if err != nil {
		beego.Error("[123云盘] 创建请求失败:", err)
		return err
	}

	// 设置请求头
	req.Header.Set("Content-Type", "application/octet-stream")

	beego.Debug("[123云盘] 请求头:", req.Header)

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		beego.Error("[123云盘] 上传文件失败:", err)
		return err
	}
	defer resp.Body.Close()

	beego.Info("[123云盘] 上传文件响应:", resp)
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("上传文件失败: %d", resp.StatusCode)
	}

	beego.Info("[123云盘] 文件上传成功")
	return nil
}

// 上传完成
func (this *Store123) completeUpload(preUploadId string) (*CompleteUploadResponse, error) {
	url := fmt.Sprintf("%s/upload/v1/file/upload_complete", this.Host)
	beego.Info("[上传完成] 请求地址:", url)

	data := map[string]interface{}{
		"preuploadID": preUploadId, // 预上传ID
	}

	beego.Debug("[上传完成] 请求数据:", data)

	resp, err := this.doRequest("POST", url, data)
	if err != nil {
		return nil, err
	}

	beego.Debug("[上传完成] 响应:", string(resp))

	var result CompleteUploadResponse
	if err := json.Unmarshal(resp, &result); err != nil {
		return nil, err
	}

	// 如果是异步处理，需要轮询获取结果
	if result.Data.Async && !result.Data.Complete {
		return this.pollUploadResult(preUploadId)
	}

	return &result, nil
}

// 轮询获取上传结果
func (this *Store123) pollUploadResult(preUploadId string) (*CompleteUploadResponse, error) {
	url := fmt.Sprintf("%s/upload/v1/file/upload_async_result", this.Host)
	maxRetries := 10                 // 最大重试次数
	retryInterval := time.Second * 2 // 重试间隔

	for i := 0; i < maxRetries; i++ {
		beego.Info("[轮询上传结果] 第", i+1, "次尝试")

		data := map[string]interface{}{
			"preuploadID": preUploadId,
		}

		resp, err := this.doRequest("POST", url, data)
		if err != nil {
			return nil, err
		}

		beego.Debug("[轮询上传结果] 响应:", string(resp))

		var result CompleteUploadResponse
		if err := json.Unmarshal(resp, &result); err != nil {
			return nil, err
		}

		// 如果上传完成，返回结果
		if result.Data.Complete {
			beego.Info("[轮询上传结果] 上传完成")
			return &result, nil
		}

		// 如果未完成，等待后重试
		beego.Info("[轮询上传结果] 等待", retryInterval)
		time.Sleep(retryInterval)
	}

	return nil, errors.New("上传结果轮询超时")
}

// 获取直链
func (this *Store123) getDirectLink(fileId string) (string, error) {
	// 构建带参数的URL
	url := fmt.Sprintf("%s/api/v1/direct-link/url?fileID=%s", this.Host, fileId)
	beego.Info("[123云盘] 获取直链请求:", url)

	// 发送GET请求
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		beego.Error("[123云盘] 创建请求失败:", err)
		return "", err
	}

	// 设置请求头
	req.Header.Set("Authorization", "Bearer "+this.Token)
	req.Header.Set("platform", "open_platform")

	beego.Debug("[123云盘] 请求头:", req.Header)

	// 发送请求
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		beego.Error("[123云盘] 发送请求失败:", err)
		return "", err
	}
	defer resp.Body.Close()

	// 读取响应内容
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		beego.Error("[123云盘] 读取响应失败:", err)
		return "", err
	}

	beego.Debug("[123云盘] 响应状态码:", resp.StatusCode)
	beego.Debug("[123云盘] 响应数据:", string(body))

	// 解析响应
	var result struct {
		Code    int    `json:"code"`
		Message string `json:"message"`
		Data    struct {
			Url string `json:"url"`
		} `json:"data"`
	}

	if err := json.Unmarshal(body, &result); err != nil {
		beego.Error("[123云盘] 解析响应失败:", err)
		return "", err
	}

	if result.Code != 0 {
		beego.Error("[123云盘] 获取直链失败:", result.Message)
		return "", fmt.Errorf("获取直链失败: %s", result.Message)
	}

	beego.Debug("[123云盘] 获取直链响应:", result)
	return result.Data.Url, nil
}

// 发送HTTP请求
func (this *Store123) doRequest(method, url string, data interface{}) ([]byte, error) {
	beego.Debug("[发送HTTP请求] 发送请求:", method, url)
	beego.Debug("[发送HTTP请求] 请求数据:", data)

	jsonData, _ := json.Marshal(data)
	req, err := http.NewRequest(method, url, bytes.NewBuffer(jsonData))
	if err != nil {
		beego.Error("[发送HTTP请求] 创建请求失败:", err)
		return nil, err
	}

	req.Header.Set("Authorization", "Bearer "+this.Token)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("platform", "open_platform")

	beego.Debug("[发送HTTP请求] 请求头:", req.Header)

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		beego.Error("[发送HTTP请求] 发送请求失败:", err)
		return nil, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		beego.Error("[发送HTTP请求] 读取响应失败:", err)
		return nil, err
	}

	beego.Debug("[发送HTTP请求] 响应状态码:", resp.StatusCode)
	beego.Debug("[发送HTTP请求] 响应数据:", string(body))

	return body, nil
}

// API响应结构体
type CreateFileResponse struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	Data    struct {
		FileId      int64  `json:"fileID"`      // 文件ID是数字类型
		PreUploadId string `json:"preuploadID"` // 预上传ID
		Reuse       bool   `json:"reuse"`       // 是否秒传
		SliceSize   int64  `json:"sliceSize"`   // 分片大小
	} `json:"data"`
}

type CompleteUploadResponse struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	Data    struct {
		FileId   int64 `json:"fileID"`    // 文件ID
		Async    bool  `json:"async"`     // 是否异步处理
		Complete bool  `json:"completed"` // 是否完成
	} `json:"data"`
}
